package DesginFotmat;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Slf4j
public class GuardedSuspendDecoupling {
    public static void main(String[] args) throws InterruptedException {
        for(int i=0;i<3;i++){
            new People().start();
        }
        Thread.sleep(1000);
        for (Integer id : MailBoxes.getIds()) {
            new PostMan(id,"内容"+id).start();
        }
    }
}

@Slf4j
class People extends Thread {
    @Override
    public void run() {
        GuardedObjectV2 guardeObject = MailBoxes.createGuardeObject();
        log.debug("开始收信id:{}", guardeObject.getId());
        Object mail = guardeObject.getResponse(5000);
        log.debug("收到信id:{},内容：{}", guardeObject.getId(), mail);
    }
}


@Slf4j
class PostMan extends Thread {
    private int id;
    private String mail;
    public PostMan(int id, String mail) {
        this.id = id;
        this.mail = mail;
    }
    @Override
    public void run() {
        GuardedObjectV2 guardedObject = MailBoxes.getGuardedObject(id);
        log.debug("送信id:{},内容：{}", id, mail);
        guardedObject.complete(mail);
    }

}

class MailBoxes{
    private static Map<Integer,GuardedObjectV2> boxes = new HashMap<>();
    private static int id =1;
    //产生唯一id
    private static synchronized int generateId(){
        return id++;
    }

    public static GuardedObjectV2 createGuardeObject(){
        GuardedObjectV2 guardedObject = new GuardedObjectV2(generateId());
        boxes.put(guardedObject.getId(), guardedObject);
        return guardedObject;
    }

    public static Set<Integer> getIds(){
        return boxes.keySet();
    }

    public static GuardedObjectV2 getGuardedObject(int id){
        return boxes.remove(id);
    }
}

class GuardedObjectV2 {

    //标识Guarded Object
    @Setter
    @Getter
    private int id;
    //结果
    private Object response;

    public GuardedObjectV2(int i) {
        this.id = i;
    }


    //获取结果
    //timeout表示要等多久
    public Object getResponse(long timeout) {
        synchronized (this) {
            //开始时间
            long begin = System.currentTimeMillis();
            //经历的时间
            long passedTime = 0;

            //没有结果
            while (response == null) {
                //这一轮循环应该等待的时间
                long waitTime = timeout - passedTime;

                //经历的时间超过了最大等待时间就退出循环
                if(waitTime<=0) {
                    break;
                }
                try {
                    this.wait(waitTime);//避免虚假唤醒导致等待时间变长
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //求得经历时间
                passedTime = System.currentTimeMillis() - begin;
            }
            return response;
        }
    }

    //产生结果
    public void complete(Object response) {
        synchronized (this) {
            //给结果成员变量赋值
            this.response = response;
            this.notifyAll();
        }
    }

}
